﻿#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using AbstractClass.Runtime.Caching;
using NUnit.Framework;

namespace QuasarTest.Core
{
    [TestFixture]
    public class CacheTest
    {
        #region Setup/Teardown

        [SetUp]
        public void Init()
        {
            _dataStore = new Dictionary<int, string>();
            _cache = new Cache<int, string>(400, new LfuEvictionPolicy<int>());

            var random = new Random();
            _stopWatch = new Stopwatch();

            _stopWatch.Start();
            for (int i = 0; i < 100000; i++)
            {
                _dataStore.Add(i, random.Next(9999999).ToString("X"));
            }
            _stopWatch.Stop();
            Console.WriteLine("Dictionary insertion time = " + _stopWatch.Elapsed);

            _cache.EvictionPolicy = new LruEvictionPolicy<int>();
            _cache.EntryExpired += CacheEntryExpired;
            _cache.Hit += CacheHit;
            _cache.Miss += CacheMiss;
        }

        #endregion

        private Dictionary<int, string> _dataStore;
        private Cache<int, string> _cache;
        private Stopwatch _stopWatch;

        private void CacheMiss(object sender, CacheEventArgs<int, string> e)
        {
            int key = e.MissedKey;
            Assert.NotNull(key);
            Assert.IsFalse(_cache.ContainsKey(key));
            _cache.AddEntry(key, _dataStore[key]);
            Assert.IsTrue(_cache.ContainsKey(key));
            Console.WriteLine("Miss for {0}", key);
        }

        private void CacheHit(object sender, CacheEventArgs<int, string> e)
        {
            int key = e.Entry.Key;
            Assert.IsTrue(_cache.ContainsKey(key));
            Console.WriteLine("Hit for {0}", e.Entry.Key);
        }

        private void CacheEntryExpired(object sender, CacheEventArgs<int, string> e)
        {
            int key = e.Entry.Key;
            Assert.IsFalse(_cache.ContainsKey(key));
            Console.WriteLine("Expired item {0}", e.Entry.Key);
        }

        [Test]
        public void GeneralCacheTest()
        {
            _stopWatch.Start();
            var random = new Random();
            for (int i = 0; i < 400; i++)
            {
                RerunLru:
                int index = random.Next(0, 100000);
                string data = _dataStore[index];
                if (_cache.ContainsKey(index))
                {
                    goto RerunLru;
                }
                _cache.AddEntry(index, data);
            }
            _stopWatch.Stop();
            Console.WriteLine("Cache insertion time = " + _stopWatch.Elapsed);

            for (int i = 0; i < 500; i++)
            {
                _cache.Fetch(random.Next(0, 100000));
            }
            Console.WriteLine(_cache.HitRatio);
        }

        [Test]
        public void LfuCacheTest()
        {
            _cache.ReDim(4);
            Assert.AreEqual(_cache.Capacity, 4);

            _cache.EvictionPolicy = new LfuEvictionPolicy<int>();

            Assert.IsFalse(_cache.ContainsKey(3));
            _cache.Fetch(3);

            Assert.IsFalse(_cache.ContainsKey(5));
            _cache.Fetch(5);

            Assert.IsFalse(_cache.ContainsKey(9));
            _cache.Fetch(9);

            Assert.IsTrue(_cache.ContainsKey(3));
            _cache.Fetch(3);

            Assert.IsTrue(_cache.ContainsKey(5));
            _cache.Fetch(5);

            Assert.IsTrue(_cache.ContainsKey(9));
            _cache.Fetch(9);

            Assert.IsFalse(_cache.ContainsKey(7));
            _cache.Fetch(7);

            Assert.IsTrue(_cache.ContainsKey(5));
            _cache.Fetch(5);

            Assert.AreEqual(_cache.Count, 4);
            // key 7 is accessed only 1 time so it will get evicted
            Assert.AreEqual(_cache.EvictionPolicy.GetVictimEntry(), 7);

            Assert.IsFalse(_cache.ContainsKey(4));
            _cache.Fetch(4);
            Assert.IsFalse(_cache.ContainsKey(7));
            Assert.IsTrue(_cache.ContainsKey(4));

            // key 4 is accessed only 1 time so it will get evicted
            Assert.AreEqual(_cache.EvictionPolicy.GetVictimEntry(), 4);
        }

        [Test]
        public void LruCacheTest()
        {
            _cache.ReDim(4);
            Assert.AreEqual(_cache.Capacity, 4);

            _cache.EvictionPolicy = new LruEvictionPolicy<int>();

            Assert.IsFalse(_cache.ContainsKey(3));
            _cache.Fetch(3);
            Thread.Sleep(10);

            Assert.IsFalse(_cache.ContainsKey(5));
            _cache.Fetch(5);
            Thread.Sleep(10);

            Assert.IsFalse(_cache.ContainsKey(9));
            _cache.Fetch(9);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(3));
            _cache.Fetch(3);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(5));
            _cache.Fetch(5);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(9));
            _cache.Fetch(9);
            Thread.Sleep(10);

            Assert.IsFalse(_cache.ContainsKey(7));
            _cache.Fetch(7);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(5));
            _cache.Fetch(5);
            Thread.Sleep(10);

            Assert.AreEqual(_cache.Count, 4);
            // key 5 is least recently used, so get evicted
            Assert.AreEqual(_cache.EvictionPolicy.GetVictimEntry(), 3);

            Assert.IsFalse(_cache.ContainsKey(4));
            _cache.Fetch(4);
            Assert.IsFalse(_cache.ContainsKey(3));
            Thread.Sleep(10);
            Assert.IsTrue(_cache.ContainsKey(4));

            // key 9 is least recently used, so get evicted
            Assert.AreEqual(_cache.EvictionPolicy.GetVictimEntry(), 9);
        }

        [Test]
        public void MruCacheTest()
        {
            _cache.ReDim(4);
            Assert.AreEqual(_cache.Capacity, 4);

            _cache.EvictionPolicy = new MruEvictionPolicy<int>();


            Assert.IsFalse(_cache.ContainsKey(3));
            _cache.Fetch(3);
            Thread.Sleep(10);

            Assert.IsFalse(_cache.ContainsKey(5));
            _cache.Fetch(5);
            Thread.Sleep(10);

            Assert.IsFalse(_cache.ContainsKey(9));
            _cache.Fetch(9);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(3));
            _cache.Fetch(3);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(5));
            _cache.Fetch(5);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(9));
            _cache.Fetch(9);
            Thread.Sleep(10);

            Assert.IsFalse(_cache.ContainsKey(7));
            _cache.Fetch(7);
            Thread.Sleep(10);

            Assert.IsTrue(_cache.ContainsKey(5));
            _cache.Fetch(5);
            Thread.Sleep(10);

            Assert.AreEqual(_cache.Count, 4);
            // key 5 is most recently used, the last accessed key, so get evicted
            Assert.AreEqual(_cache.EvictionPolicy.GetVictimEntry(), 5);

            Assert.IsFalse(_cache.ContainsKey(4));
            _cache.Fetch(4);
            Assert.IsFalse(_cache.ContainsKey(5));
            Thread.Sleep(10);
            Assert.IsTrue(_cache.ContainsKey(4));

            // key 4 is most recently used, the last accessed key, so get evicted
            Assert.AreEqual(_cache.EvictionPolicy.GetVictimEntry(), 4);
        }
    }
}